package com.reactsdk.sid.main

import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import androidx.fragment.app.FragmentActivity
import com.facebook.react.bridge.Callback
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.WritableNativeMap
import com.reactsdk.sid.SIDUtils.INVALID_PARAMS
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import sberid.sdk.auth.model.StandName
//import sberid.sdk.global.models.StandName
import sberid.sdk.global.models.CustomFonts
import sberid.sdk.global.models.LinkInformation
import sberid.sdk.global.models.OIDCParams
import sberid.sdk.init.SID
import sberid.sdk.ui.models.root.UIColorMode

class SIDModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {

	companion object {

		private const val NAME = "SIDModule"
		private const val STATE = "state"
		private const val NONCE = "nonce"
		private const val SCOPE = "scope"
		private const val CLIENT_ID = "client_id"
		private const val CODE = "code"
		private const val IS_SUCCESS = "is_success"
		private const val ERROR_DESCRIPTION = "error_description"
		private const val ERROR_CODE = "error_code"
		private const val PARTNER_NAME = "partner_name"
		private const val THEME_COLOR_LIGHT = "theme_color_light"
		private const val THEME_COLOR_DARK = "theme_color_dark"
		private const val PARTNER_PROFILE_URL = "partner_profile_url"
		private const val IS_SHOW_ERROR_ON_MAIN = "is_show_error_on_main"
		private const val REDIRECT_URI = "redirect_uri"
		private const val CODE_CHALLENGE = "code_challenge"
		private const val CODE_CHALLENGE_METHOD = "code_challenge_method"
		private const val CUSTOM_TAB_REDIRECT_URI = "custom_tab_redirect_uri"
		private const val URI_SCHEME = "uri_scheme"
		private const val LOGIN_HINT = "login_hint"
		private const val PARTNER_COLOR_INT = "partner_color_int"
		private const val OPEN_IN = "open_in"
		private const val PARTNER_LINK = "partner_link"
		private const val OIDC_URL = "oidc_url"
		private const val TYPE_AUTH = "type_auth"
		private const val CREATE_TIME_APP_TOKEN = "create_time_app_token"
		private const val UI_COLOR_MODE = "ui_color_mode"
		private const val MEDIUM_RES_ID = "medium_res_id"
		private const val SEMI_BOLD_RES_ID = "semi_bold_res_id"

		private const val PSI_CLOUD_STAND = "psi_cloud"
		private const val PROM_STAND = "prom"
		private const val PSI_STAND = "psi"
		private const val IFT_STAND = "ift"
		private const val IFT_CLOUD_STAND = "ift_cloud"

		private const val LIGHT = "light"
		private const val DARK = "dark"
		private const val AUTO = "auto"

		private const val EMPTY_ERROR = "IS EMPTY"
		private const val CREATE_LOGIN_MANAGER_ERROR =
			"You can use there stands - $PROM_STAND, $PSI_STAND, $PSI_CLOUD_STAND, $IFT_STAND, $IFT_CLOUD_STAND"
		private const val CONTEXT_ERROR = "There isn't any context"
		private const val ACTIVITY_ERROR = "There isn't any activity"
		private const val APP_TOKEN_ERROR = "There isn't any token"
		private const val OPTION_ERROR = "There isn't correct option"
	}

	override fun getName(): String = NAME
	private var clientID = ""
	private var partnerName: String? = null
	private var themeColorLight: Int? = null
	private var themeColorDark: Int? = null
	private var partnerProfileUrl: String? = null
	private var isShowErrorOnMain: Boolean = false
	private var standType: String = PROM_STAND

	//Initializer


	@ReactMethod
	fun initialize(
	    clientId: String,
        partnerName: String?,
        themeColorLight: String?,
        themeColorDark: String?,
        partnerProfileUrl: String?,
        isShowErrorOnMain: Boolean,
	    standType: String
	) {
		this.clientID = clientId
		this.partnerName= partnerName
		this.partnerProfileUrl = partnerProfileUrl
		this.themeColorLight = kotlin.runCatching { Color.parseColor(themeColorLight) }.getOrNull()
		this.themeColorDark = kotlin.runCatching { Color.parseColor(themeColorDark) }.getOrNull()
		this.isShowErrorOnMain = isShowErrorOnMain
		this.standType = PROM_STAND
		val applicationContext = currentActivity?.application ?: return

		SID.Initializer.initialize(
			application = applicationContext,
			clientId = clientId,
			partnerName = partnerName,
			partnerProfileUrl = partnerProfileUrl,
			themeColorLight = this.themeColorLight,
			themeColorDark = this.themeColorDark,
			isShowErrorOnMain = isShowErrorOnMain
		)

		createLoginManager(nameOfStand = standType)
	}

	//Login

	@ReactMethod
	private fun createLoginManager(nameOfStand: String) {
		val stand = when (nameOfStand) {
			PROM_STAND -> StandName.PROM
			PSI_STAND -> StandName.ESA_PSI
			PSI_CLOUD_STAND -> StandName.CLOUD_PSI
			IFT_STAND -> StandName.ESA_IFT
			IFT_CLOUD_STAND -> StandName.CLOUD_IFT
			else -> StandName.PROM
		} ?: return

		SID.Login.createLoginManager(
			stand
		)
	}

	@ReactMethod
	private fun createUriForLogin(
		clientId: String,
		scope: String,
		state: String,
		nonce: String,
		redirectUri: String,
		customTabRedirectUri: String?,
		codeChallenge: String?,
		codeChallengeMethod: String?,
		uriScheme: String?,
		loginHint: String?,
	): String {
		return SID.Login.createUriForLogin(
			clientId = clientId,
			scope = scope,
			state = state,
			nonce = nonce,
			redirectUri = redirectUri,
			customTabRedirectUri = customTabRedirectUri,
			codeChallenge = codeChallenge,
			codeChallengeMethod = codeChallengeMethod,
			uriScheme = null,
			loginHint = loginHint
		).toString()
	}

	@ReactMethod
	private fun loginWithID(uri: Uri) {
		val activity = currentActivity as? FragmentActivity ?: run {
			loginWithIDContext(uri, currentActivity?.applicationContext)
			return
		}

		SID.Login.loginWithID(
			activity,
			uri
		)
	}

	@ReactMethod
	fun auth(
		scope: String,
		state: String,
		nonce: String,
		redirectUri: String,
		ssoBaseUrl: String?, //хз что за параметр
		codeChallenge: String?,
		codeChallengeMethod: String?,
		loginHint: String?
	){
		val url = SID.Login.createUriForLogin(
			clientId = clientID,
			scope = scope,
			state = state,
			nonce = nonce,
			redirectUri = redirectUri,
			uriScheme = ssoBaseUrl, //Не знаю куда тут это поставить расхождение на платформах
			codeChallenge = codeChallenge,
			codeChallengeMethod = codeChallengeMethod,
			loginHint = loginHint
		)
		loginWithID(url)
	}

	private fun loginWithIDContext(uri: Uri, applicationContext: Context?) {
		val context = applicationContext ?: return

		SID.Login.loginWithID(
			context,
			uri
		)
	}

	@ReactMethod
	fun loginWithIDWeb(
		scope: String,
		state: String,
		nonce: String,
		redirectUri: String,
		svcRedirectUrlString: String,
		ssoBaseUrl: String? = null,
		codeChallenge: String? = null,
		codeChallengeMethod: String? = null,
		loginHint: String? = null,
		callback: Callback
	) {
		val context = currentActivity?.application ?: run {
			callback(false)
			return
		}

		val url = SID.Login.createUriForLogin(
			clientId = clientID,
			scope = scope,
			state = state,
			nonce = nonce,
			redirectUri = redirectUri,
			customTabRedirectUri = svcRedirectUrlString,
			codeChallenge = codeChallenge,
			codeChallengeMethod = codeChallengeMethod,
			loginHint = loginHint
		)

		SID.Login.loginWithIDToCustomTab(
			context,
			url
		)
	}

	@ReactMethod
	fun logout() {
		SID.Login.logout()
	}

	@ReactMethod
	fun autoAuth(
		scope: String,
		state: String,
		nonce: String,
		redirectUri: String,
		ssoBaseUrl: String? = null,
		codeChallenge: String? = null,
		codeChallengeMethod: String? = null,
		loginHint: String? = null,
		callback: Callback
	) {
		val context = currentActivity?.application ?: run {
			callback(false)
			return
		}

		val url = SID.Login.createUriForLogin(
			clientId = clientID,
			scope = scope,
			state = state,
			nonce = nonce,
			redirectUri = redirectUri,
			customTabRedirectUri = ssoBaseUrl,
			codeChallenge = codeChallenge,
			codeChallengeMethod = codeChallengeMethod,
			loginHint = loginHint
		)

/*		val authResult = SID.Login.autoAuth(
			context,
			url
		)

		callback(authResult)*/
	}

	@ReactMethod
	fun getIDAuthResult(
		deepLink: String,
		completion: Callback
	) {
		val newIntent = Intent().apply {
			data = Uri.parse(deepLink)
		}
		val result = SID.Login.getIDAuthResult(newIntent)

		val resultMap = WritableNativeMap().apply {
			putString(CODE, result.authCode)
			putString(STATE, result.state)
			putString(NONCE, result.nonce)
			putBoolean(IS_SUCCESS, result.isSuccess ?: false)
			putString(ERROR_DESCRIPTION, result.errorDescription)
			putString(ERROR_CODE, result.errorCode)
		}
		completion(resultMap)
	}

	@ReactMethod
	fun sendOtherAuth(nameAuthService: String, isSuccess: Boolean = true, isUserNew: Boolean? = null) {
		SID.Login.sendOtherAuth(nameAuthService, isSuccess, isUserNew)
	}

	@ReactMethod
	fun getSeamlessUriScheme(uri: String): String? {
		return SID.Login.getSeamlessUriScheme(uri = uri.toUri())
	}

	@ReactMethod
	fun setUserID(userID: String) {
		SID.Login.setUserID(userID = userID)
	}

	@ReactMethod
	fun setClientID(clientId: String) {
		val applicationContext = currentActivity?.application ?: return
		SID.Initializer.initialize(
			application = applicationContext,
			clientId = clientId,
			partnerName = partnerName,
			partnerProfileUrl = partnerProfileUrl,
			themeColorLight = themeColorLight,
			themeColorDark = themeColorDark,
			isShowErrorOnMain = isShowErrorOnMain
		)
	}


	//AppToken
	@ReactMethod
	fun updateAppToken(
		scope: String,
		clientId: String,
		state: String,
		nonce: String,
		redirectUri: String,
		codeChallenge: String?,
		codeChallengeMethod: String?,
		completion: Callback
	) {
		val params = OIDCParams(
			scope = scope,
			clientId = clientId,
			state = state,
			nonce = nonce,
			redirectUri = redirectUri,
			codeChallenge = codeChallenge,
			codeChallengeMethod = codeChallengeMethod
		)
		SID.AppToken.updateAppToken(params)
	}

	@ReactMethod
	fun checkAppTokenForValid(completion: Callback) {
		SID.AppToken.checkAppTokenForValid()
	}

	@ReactMethod
	fun subscribeToAppTokenEvents(callback: Callback) {
		val activity = currentActivity as? AppCompatActivity ?: run {
			callback(INVALID_PARAMS + ACTIVITY_ERROR)
			return
		}
		SID.AppToken.subscribeToEvents(activity) { event ->
			callback(event.toString())
		}
	}

	@ReactMethod
	fun goToPartnerSSO(
		partnerWebLink: String,
		partnerClientId: String?,
		partnerColor: String?,
		openIn: String?,
		completion: Callback
	) {
		val activity = currentActivity as? FragmentActivity ?: run {
			return
		}
		val linkInformation = LinkInformation(
			webLink = partnerWebLink,
			clientId = partnerClientId,
			partnerColorInt = partnerColor?.toIntOrNull(),
			openIn = openIn
		)
		SID.AppToken.goToPartnerSSO(activity, linkInformation)
	}

	@ReactMethod
	fun getApp2SdkToken(
		clientId: String,
		partnerLink: String,
		callback: Callback
	) {
		CoroutineScope(Dispatchers.IO).launch {
			val result = SID.AppToken.getApp2SdkToken(clientId = clientId, partnerLink = partnerLink) ?: run {
				callback(EMPTY_ERROR + APP_TOKEN_ERROR)
				return@launch
			}
			val resultMap = WritableNativeMap().apply {
				putString(OIDC_URL, result.oidcUrl)
				putString(TYPE_AUTH, result.typeAuth.toString())
				putDouble(CREATE_TIME_APP_TOKEN, result.createTimeAppToken.toDouble())
			}
			callback(resultMap)
		}
	}

	//ELK
	@ReactMethod
	fun subscribeToAnalytic(callback: Callback) {
		val activity = currentActivity as? AppCompatActivity ?: run {
			callback(INVALID_PARAMS + ACTIVITY_ERROR)
			return
		}
		SID.ELK.subscribeToAnalytic(activity) { event ->
			callback(event)
		}
	}

	@ReactMethod
	fun setUIMode(
		uiModeString: String
	) {
		val uiMode = when (uiModeString) {
			LIGHT -> UIColorMode.LIGHT
			DARK -> UIColorMode.DARK
			AUTO -> UIColorMode.AUTO
			else -> return
		}

		SID.ELK.setUIMode(uiMode)
	}

	@ReactMethod
	fun setCustomFonts(
		mediumResID: Int?,
		semiBoldResID: Int?,
		callback: Callback
	) {
		val customFonts = CustomFonts(
			semiBoldResID = semiBoldResID,
			mediumResID = mediumResID
		)
		SID.ELK.setCustomFonts(customFonts)
	}

}